package com.mattc.autotyper.gui.fx; import static com.mattc.autotyper.gui.fx.FXGuiUtils.makeAlwaysOnTop; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.ButtonBar; import javafx.scene.control.ComboBox; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import javafx.scene.web.WebView; import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.StageStyle; import javafx.stage.Window; import com.mattc.autotyper.Strings; import com.mattc.autotyper.meta.FXCompatible; import com.mattc.autotyper.meta.ModeParser; import com.mattc.autotyper.meta.ModeParser.Mode; import com.mattc.autotyper.util.Console; import com.mattc.autotyper.util.IOUtils; import java.io.IOException; import java.nio.file.Path; import java.util.prefs.Preferences; @FXCompatible public class FXConfirmDialog extends Stage { private static final String[] THEMES = {"neat", "blackboard"}; private static final String DEFAULT_SYNTAX_LUA = "Lua"; private static final Preferences prefs = Preferences.userNodeForPackage(FXAutotyperWindow.class); private static final int BUTTON_WIDTH = 200; private final WebView webView = new WebView(); private volatile boolean approved = false; /** * a template for editing code - this can be changed to any template derived of * the supported modes at http://codemirror.net to allow syntax highlighted * editing of a wide variety of languages. */ private static final String EDITING_TEMPLATE = "<!doctype html>" + "<html>" + "<head>" + " <link rel=\"stylesheet\" href=\"http://codemirror.net/lib/codemirror.css\">" + " <link rel=\"stylesheet\" href=\"http://codemirror.net/theme/neat.css\">" + " <link rel=\"stylesheet\" href=\"http://codemirror.net/theme/blackboard.css\">" + " <script src=\"http://codemirror.net/lib/codemirror.js\"></script>" + " <script src=\"http://codemirror.net/mode/clike/clike.js\"></script>" + " <script src=\"http://codemirror.net/mode/%1$s/%1$s.js\"></script>" + "</head>" + "<body>" + "<form><textarea id=\"code\" name=\"code\">\n" + "%2$s" + "</textarea></form>" + "<script>" + " var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {" + " lineNumbers: true," + " matchBrackets: true," + " theme: \"%3$s\"," + " mode: \"%4$s\"," + " readOnly: true" + " });" + "</script>" + "</body>" + "</html>"; private String codeSnapshot; private String theme = "neat"; private Mode mode = ModeParser.getModeFor(DEFAULT_SYNTAX_LUA); private FXConfirmDialog(Window owner, String code) { super(StageStyle.UTILITY); loadPrefs(); this.codeSnapshot = code; setTitle("Code Confirmation"); setResizable(false); initOwner(owner); initModality(Modality.WINDOW_MODAL); this.webView.setPrefSize(650, 325); this.webView.getEngine().loadContent(getFormattedTemplate()); final Scene scene = new Scene(assemble()); scene.getStylesheets().addAll(owner.getScene().getStylesheets()); setScene(scene); toFront(); centerOnScreen(); makeAlwaysOnTop(this); showAndWait(); savePrefs(); } public static boolean confirm(Window owner, Path code) throws IOException { return confirm(owner, IOUtils.fileToString(code.toFile())); } public static boolean confirm(Window owner, String code) { final FXConfirmDialog dialog = new FXConfirmDialog(owner, code); return dialog.isApproved(); } public static boolean isAvailable() { try { return IOUtils.checkConnectionSuccess("http://codemirror.net"); } catch (final Exception e) { Console.exception(e); return false; } } private void savePrefs() { prefs.put(Strings.PREFS_CONFIRM_THEME, this.theme); prefs.put(Strings.PREFS_CONFIRM_MODE, this.mode.displayName); } private void loadPrefs() { this.theme = prefs.get(Strings.PREFS_CONFIRM_THEME, this.theme); this.mode = ModeParser.getModeFor(prefs.get(Strings.PREFS_CONFIRM_MODE, DEFAULT_SYNTAX_LUA)); } private Parent assemble() { final BorderPane root = new BorderPane(); final HBox btnBox = new HBox(50); final Button approve = new Button("Approve"); final Button reject = new Button("Reject"); approve.setPrefWidth(BUTTON_WIDTH); reject.setPrefWidth(BUTTON_WIDTH); ButtonBar buttonBar = new ButtonBar(); buttonBar.getButtons().addAll(approve, reject); ButtonBar.setButtonData(approve, ButtonBar.ButtonData.YES); ButtonBar.setButtonData(reject, ButtonBar.ButtonData.NO); approve.setDefaultButton(true); btnBox.setId("button-box"); btnBox.setAlignment(Pos.BASELINE_CENTER); btnBox.setPadding(new Insets(10, 0, 10, 0)); btnBox.getChildren().addAll(buttonBar); final VBox webBox = new VBox(); webBox.getChildren().add(this.webView); final HBox optBox = new HBox(20); final Button cpyBtn = new Button("Copy Code"); final ComboBox<String> themeColor = new ComboBox<>(FXCollections.observableArrayList(THEMES)); final ComboBox<String> syntaxes = new ComboBox<>(FXCollections.observableList(ModeParser.getPossibleModes())); syntaxes.getSelectionModel().select(this.mode.displayName); themeColor.getSelectionModel().select(this.theme); cpyBtn.setPrefWidth(BUTTON_WIDTH); syntaxes.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue) -> { if (newValue == null) return; FXConfirmDialog.this.mode = ModeParser.getModeFor(newValue); FXConfirmDialog.this.webView.getEngine().loadContent(getFormattedTemplate()); }); themeColor.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue) -> { if (newValue == null) return; FXConfirmDialog.this.theme = newValue.toLowerCase(); FXConfirmDialog.this.webView.getEngine().loadContent(getFormattedTemplate()); }); optBox.setId("menu-bar"); optBox.setAlignment(Pos.CENTER_LEFT); optBox.setPadding(new Insets(10, 0, 10, 10)); optBox.getChildren().addAll(cpyBtn, syntaxes, themeColor); root.setTop(optBox); root.setCenter(webBox); root.setBottom(btnBox); VBox.setVgrow(this.webView, Priority.ALWAYS); syntaxes.setEditable(false); approve.setOnAction((e) -> { FXConfirmDialog.this.approved = true; hide(); }); reject.setOnAction((e) -> { FXConfirmDialog.this.approved = false; hide(); }); cpyBtn.setOnAction((e) -> { final String code = getCodeAndSnapshot(); final Clipboard clip = Clipboard.getSystemClipboard(); final ClipboardContent content = new ClipboardContent(); content.putString(code); Platform.runLater(() -> clip.setContent(content)); }); return root; } public void setCode(String code) { this.codeSnapshot = code; refresh(); } public String getCodeAndSnapshot() { this.codeSnapshot = (String) this.webView.getEngine().executeScript("editor.getValue();"); return this.codeSnapshot; } public boolean isApproved() { return this.approved; } private String getFormattedTemplate() { return String.format(EDITING_TEMPLATE, this.mode.name, this.codeSnapshot, this.theme, this.mode.mimeType); } private void refresh() { this.webView.getEngine().loadContent(getFormattedTemplate()); } }